global.tft = (function () {
    const TFT_MOSI = 19;
    const TFT_SCLK = 18;
    const TFT_CS = 5;
    const TFT_DC = 16;
    const TFT_RST = 23;
    const TFT_BL = 4;
    const PARALLEL_LINES = 16;

    const ST7789_NOP = '\x00';
    const ST7789_SWRESET = '\x01';
    const ST7789_RDDID = '\x04';
    const ST7789_RDDST = '\x09';

    const ST7789_RDDPM = '\x0A';      // Read display power mode
    const ST7789_RDD_MADCTL = '\x0B';      // Read display MADCTL
    const ST7789_RDD_COLMOD = '\x0C';      // Read display pixel format
    const ST7789_RDDIM = '\x0D';      // Read display image mode
    const ST7789_RDDSM = '\x0E';      // Read display signal mode
    const ST7789_RDDSR = '\x0F';      // Read display self-diagnostic result (ST7789V)

    const ST7789_SLPIN = '\x10';
    const ST7789_SLPOUT = '\x11';
    const ST7789_PTLON = '\x12';
    const ST7789_NORON = '\x13';

    const ST7789_INVOFF = '\x20';
    const ST7789_INVON = '\x21';
    const ST7789_GAMSET = '\x26';      // Gamma set
    const ST7789_DISPOFF = '\x28';
    const ST7789_DISPON = '\x29';
    const ST7789_CASET = '\x2A';
    const ST7789_RASET = '\x2B';
    const ST7789_RAMWR = '\x2C';
    const ST7789_RGBSET = '\x2D';      // Color setting for 4096, 64K and 262K colors
    const ST7789_RAMRD = '\x2E';

    const ST7789_PTLAR = '\x30';
    const ST7789_VSCRDEF = '\x33';      // Vertical scrolling definition (ST7789V)
    const ST7789_TEOFF = '\x34';      // Tearing effect line off
    const ST7789_TEON = '\x35';      // Tearing effect line on
    const ST7789_MADCTL = '\x36';      // Memory data access control
    const ST7789_IDMOFF = '\x38';      // Idle mode off
    const ST7789_IDMON = '\x39';      // Idle mode on
    const ST7789_RAMWRC = '\x3C';      // Memory write continue (ST7789V)
    const ST7789_RAMRDC = '\x3E';      // Memory read continue (ST7789V)
    const ST7789_COLMOD = '\x3A';

    const ST7789_RAMCTRL = '\xB0';      // RAM control
    const ST7789_RGBCTRL = '\xB1';      // RGB control
    const ST7789_PORCTRL = '\xB2';      // Porch control
    const ST7789_FRCTRL1 = '\xB3';      // Frame rate control
    const ST7789_PARCTRL = '\xB5';      // Partial mode control
    const ST7789_GCTRL = '\xB7';      // Gate control
    const ST7789_GTADJ = '\xB8';      // Gate on timing adjustment
    const ST7789_DGMEN = '\xBA';      // Digital gamma enable
    const ST7789_VCOMS = '\xBB';      // VCOMS setting
    const ST7789_LCMCTRL = '\xC0';      // LCM control
    const ST7789_IDSET = '\xC1';      // ID setting
    const ST7789_VDVVRHEN = '\xC2';      // VDV and VRH command enable
    const ST7789_VRHS = '\xC3';      // VRH set
    const ST7789_VDVSET = '\xC4';      // VDV setting
    const ST7789_VCMOFSET = '\xC5';      // VCOMS offset set
    const ST7789_FRCTR2 = '\xC6';      // FR Control 2
    const ST7789_CABCCTRL = '\xC7';      // CABC control
    const ST7789_REGSEL1 = '\xC8';      // Register value section 1
    const ST7789_REGSEL2 = '\xCA';      // Register value section 2
    const ST7789_PWMFRSEL = '\xCC';      // PWM frequency selection
    const ST7789_PWCTRL1 = '\xD0';      // Power control 1
    const ST7789_VAPVANEN = '\xD2';      // Enable VAP/VAN signal output
    const ST7789_CMD2EN = '\xDF';      // Command 2 enable
    const ST7789_PVGAMCTRL = '\xE0';      // Positive voltage gamma control
    const ST7789_NVGAMCTRL = '\xE1';      // Negative voltage gamma control
    const ST7789_DGMLUTR = '\xE2';      // Digital gamma look-up table for red
    const ST7789_DGMLUTB = '\xE3';      // Digital gamma look-up table for blue
    const ST7789_GATECTRL = '\xE4';      // Gate control
    const ST7789_SPI2EN = '\xE7';      // SPI2 enable
    const ST7789_PWCTRL2 = '\xE8';      // Power control 2
    const ST7789_EQCTRL = '\xE9';      // Equalize time control
    const ST7789_PROMCTRL = '\xEC';      // Program control
    const ST7789_PROMEN = '\xFA';      // Program mode enable
    const ST7789_NVMSET = '\xFC';      // NVM setting
    const ST7789_PROMACT = '\xFE';      // Program action

    var tft = {};
    var pack = String.fromCharCode;

    function spiWriteCommand(cmd) {
        gpio.setLevel(TFT_DC, 0);
        spi.writeData(tft.Handle, cmd, cmd.length);
    }

    function spiWriteData(data) {
        gpio.setLevel(TFT_DC, 1);
        spi.writeData(tft.Handle, data, data.length);
    }

    function openLight() {
        gpio.setOutput(TFT_BL);
        gpio.setLevel(TFT_BL, 1);
    }

    function closeLight() {
        gpio.setOutput(TFT_BL);
        gpio.setLevel(TFT_BL, 0);
    }

    tft.light = {
        on: openLight,
        off: closeLight
    }

    tft.fillRect = function (left, right, top, bottom, color) {
        spiWriteCommand(ST7789_CASET);
        colInfo = pack((left >> 8) & 0xFF)
            + pack(left & 0xFF)
            + pack((right >> 8) & 0xFF)
            + pack(right & 0xFF);
        spiWriteData(colInfo);

        spiWriteCommand(ST7789_RASET);
        pgInfo = pack((top >> 8) & 0xFF)
            + pack(top & 0xFF)
            + pack((bottom >> 8) & 0xFF)
            + pack(bottom & 0xFF);
        spiWriteData(pgInfo);

        spiWriteCommand(ST7789_RAMWR);
        var w = right - left;
        var h = bottom - top;
        for (var i = 0; i < h; i++) {
            spiWriteData(color.repeat(w));
        }
    }

    tft.init = function () {
        tft.Handle = spi.init(-1, TFT_MOSI, TFT_SCLK, TFT_CS,
            PARALLEL_LINES * 320 * 2 + 8,
            6000000
        );

        gpio.setOutput(TFT_DC);
        gpio.setOutput(TFT_RST);
        gpio.setOutput(TFT_BL);

        gpio.setLevel(TFT_RST, 1);
        os.sleepMs(5);
        gpio.setLevel(TFT_RST, 0);
        os.sleepMs(20);
        gpio.setLevel(TFT_RST, 1);

        // Sleep out
        spiWriteCommand(ST7789_SLPOUT);
        os.sleepMs(120);

        // Normal display mode on
        spiWriteCommand(ST7789_NORON);

        // Display and color format setting
        spiWriteCommand(ST7789_MADCTL);
        spiWriteData('\x08');

        // JLX240 display datasheet
        spiWriteCommand('\xB6\x0A\x82');

        spiWriteCommand(ST7789_COLMOD);
        spiWriteData('\x55');
        os.sleepMs(10);

        //ST7789V Frame rate setting
        spiWriteCommand(ST7789_PORCTRL);
        spiWriteData('\x0c\x0c\x00\x33\x33');

        spiWriteCommand(ST7789_GCTRL);      // Voltages: VGH / VGL
        spiWriteData('\x35');

        //ST7789V Power setting
        spiWriteCommand(ST7789_VCOMS);
        spiWriteData('\x28');		// JLX240 display datasheet

        spiWriteCommand(ST7789_LCMCTRL);
        spiWriteData('\x0C');

        spiWriteCommand(ST7789_VDVVRHEN);
        spiWriteData('\x01\xFF');

        spiWriteCommand(ST7789_VRHS);       // voltage VRHS
        spiWriteData('\x10');

        spiWriteCommand(ST7789_VDVSET);
        spiWriteData('\x20');

        spiWriteCommand(ST7789_FRCTR2);
        spiWriteData('\x0f');

        spiWriteCommand(ST7789_PWCTRL1);
        spiWriteData('\xa4\xa1');

        //ST7789V gamma setting
        spiWriteCommand(ST7789_PVGAMCTRL);
        spiWriteData('\xd0\x00\x02\x07\x0a\x28\x32\x44\x42\x06\x0e\x12\x14\x17');

        spiWriteCommand(ST7789_NVGAMCTRL);
        spiWriteData('\xd0\x00\x02\x07\x0a\x28\x31\x54\x47\x0e\x1c\x17\x1b\x1e');

        spiWriteCommand(ST7789_INVON);

        spiWriteCommand(ST7789_CASET);    // Column address set
        spiWriteData('\x00\x00\x00\xE5');    // 239

        spiWriteCommand(ST7789_RASET);    // Row address set
        spiWriteData('\x00\x00\x01\x3F');    // 319

        spiWriteCommand(ST7789_DISPON);    //Display on
        os.sleepMs(120);

        openLight();
    }

    return tft;
})();